package org.ewaf.framework.struts2.spring;

import java.util.Map;

import javax.servlet.ServletContext;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import org.apache.struts2.StrutsConstants;
import org.apache.struts2.config.DefaultSettings;

import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.inject.Inject;

@SuppressWarnings("unchecked")
public class AutowiringObjectFactory extends ObjectFactory {

	private static final long serialVersionUID = 687775430723539253L;

	private final static Logger log = Logger
			.getLogger(AutowiringObjectFactory.class);

	private ApplicationContext appContext;
	private AutowireCapableBeanFactory autoWiringFactory;
	private int autowireStrategy = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;

	@Inject
	public AutowiringObjectFactory(@Inject ServletContext servletContext) {
		init(servletContext);
	}

	@SuppressWarnings("deprecation")
	public void init(ServletContext servletContext) {
		appContext = WebApplicationContextUtils
				.getWebApplicationContext(servletContext);
		if (appContext == null) {
			String message = "********** FATAL ERROR STARTING UP SPRING-WEBWORK INTEGRATION **********\n"
					+ "Looks like the Spring listener was not configured for your web app! \n"
					+ "Nothing will work until WebApplicationContextUtils returns a valid ApplicationContext.\n"
					+ "You might need to add the following to web.xml: \n"
					+ "    <listener>\n"
					+ "        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n"
					+ "    </listener>";

			log.fatal(message);
			return;
		}

		autoWiringFactory = findAutoWiringBeanFactory(appContext);

		String autoWire = DefaultSettings
				.get(StrutsConstants.STRUTS_OBJECTFACTORY_SPRING_AUTOWIRE);
		if ("name".equals(autoWire)) {
			autowireStrategy = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
		} else if ("type".equals(autoWire)) {
			autowireStrategy = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
		} else if ("auto".equals(autoWire)) {
			autowireStrategy = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
		} else if ("constructor".equals(autoWire)) {
			autowireStrategy = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
		}
	}

	private AutowireCapableBeanFactory findAutoWiringBeanFactory(
			ApplicationContext context) {
		if (context instanceof AutowireCapableBeanFactory) {
			return (AutowireCapableBeanFactory) context;
		} else if (context instanceof ConfigurableApplicationContext) {
			return ((ConfigurableApplicationContext) context).getBeanFactory();
		} else if (context.getParent() != null) {
			return findAutoWiringBeanFactory(context.getParent());
		}

		return null;
	}

	public Object buildBean(Class clazz, Map extraContext) throws Exception {
		Object bean = super.buildBean(clazz, extraContext);
		if (autoWiringFactory != null) {
			autoWiringFactory.autowireBeanProperties(bean, autowireStrategy,
					false);
		}
		if (bean instanceof ApplicationContextAware) {
			((ApplicationContextAware) bean).setApplicationContext(appContext);
		}
		return bean;
	}

}
